home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / daemons / nfs / nfs-serv.2be / nfs-serv / nfs-server-2.2beta16 / ugid_map.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-28  |  14.7 KB  |  642 lines

  1. /* UNFSD - copyright Mark A Shand, May 1988.
  2.  * This software maybe be used for any purpose provided
  3.  * the above copyright notice is retained.  It is supplied
  4.  * as is, with no warranty expressed or implied.
  5.  *
  6.  * Redone from ground up by Olaf Kirch, April 1995.
  7.  *
  8.  * TODO: 
  9.  *  -    time out uids/gids.
  10.  *  -    Write protocol version 2 to allow bulk transfers and
  11.  *    some more intelligent form of authentication.
  12.  *
  13.  *    Authors:
  14.  *        Mark A. Shand
  15.  *        Olaf Kirch, <okir@monad.swb.de>
  16.  */
  17.  
  18. #include <sys/socket.h>
  19. #include <netinet/in.h>
  20. #include <arpa/inet.h>
  21. #include <pwd.h>
  22. #include <grp.h>
  23. #include <fcntl.h>
  24. #include "nfsd.h"
  25. #include "ugid.h"
  26.  
  27. #define UID_BITS    (sizeof(uid_t) * CHAR_BIT)
  28. #define UID_MAX        (1L << UID_BITS)
  29. #define UID_LIM1    (UID_MAX/2)
  30. #define GID_BITS    (sizeof(gid_t) * CHAR_BIT)
  31. #define GID_MAX        (1L << GID_BITS)
  32. #define GID_LIM1    (GID_MAX/2)
  33.  
  34. /* This structure holds a uid/gid map. 
  35.  * We do a little hack here so that we're able to hold ids at the lower 
  36.  * end of the id space as well as those at the upper end (e.g. nobody)
  37.  * without wasting precious K's inbetween.
  38.  */
  39. typedef struct ugid_map {
  40.     struct uid_map {
  41.         uid_t    *bot, *top;
  42.         int    bot_lim, top_lim;
  43.     } uid, rev_uid;    
  44.     struct gid_map {
  45.         gid_t    *bot, *top;
  46.         int    bot_lim, top_lim;
  47.     } gid, rev_gid;    
  48. } ugid_map;
  49.  
  50. /* ugidd client handle cache (indexed by ugidd hostaddr).
  51.  * 
  52.  * MAXCACHE is the number of ugidd client handles cached. 
  53.  * EXPCACHE defines (in seconds) how long invalid client handles
  54.  * are cached. Otherwise, crashed ugidd servers would hang nfsd
  55.  * during each lookup - a condition from which it would hardly
  56.  * recover, because the nfs client will keep retransmitting the
  57.  * same request over and over while nfsd still waits for ugidd
  58.  * to reply.
  59.  */
  60. #define MAXCACHE    32
  61. #define EXPCACHE    (15 * 60)
  62.  
  63. typedef struct clnt_cache {
  64.     struct in_addr    addr;        /* ugidd host addr. */
  65.     time_t        age, lru;    /* create and access time */
  66.     CLIENT        *clnt;        /* the client itself */
  67. } clnt_cache;
  68. static clnt_cache    cache[MAXCACHE];
  69. static int        initialized = 0;
  70.  
  71. /*
  72.  * Prototypes and the like
  73.  */
  74. static _PRO(ugid_map *getmap, (nfs_mount *mountp)            );
  75. static _PRO(uid_t *get_uid_mapent, (struct uid_map *map, uid_t uid, int));
  76. static _PRO(gid_t *get_gid_mapent, (struct gid_map *map, gid_t gid, int));
  77. static _PRO(int rlookup, (char *nam, int *id, int map, SVCXPRT *xprt)    );
  78. static _PRO(CLIENT *getclnt, (SVCXPRT *xprt)                );
  79. static _PRO(void delete_clnt, (CLIENT *clnt)                );
  80.  
  81. #ifdef sun
  82. void xdr_free() {}
  83. #endif
  84.  
  85. /*
  86.  * Map a server uid to a client uid
  87.  */
  88. uid_t ruid(uid, mountp, rqstp)
  89. uid_t        uid;
  90. nfs_mount    *mountp;
  91. struct svc_req    *rqstp;
  92. {
  93.     struct passwd    *pw;
  94.     ugid_map    *map;
  95.     uid_t        *up, *upr;
  96.     int        id;
  97.  
  98. #ifdef DOSHACKS
  99.     /* Reverse effects of all_squash for DOS clients */
  100.     if (mountp->o.all_squash && uid == mountp->o.nobody_uid)
  101.         return cred_uid;
  102. #endif
  103.     if (mountp->o.uidmap == identity)
  104.         return uid;
  105.  
  106.     map = getmap(mountp);
  107.     up  = get_uid_mapent(&(map->uid), uid, 1);
  108.     if (*up == AUTH_UID_NONE) {
  109.         if ((pw = getpwuid(uid)) != NULL &&
  110.             rlookup(pw->pw_name, &id, NAME_UID, rqstp->rq_xprt) &&
  111.             (id != AUTH_UID_NONE)) {
  112.             *up = (uid_t)id;
  113.             upr = get_uid_mapent(&(map->uid), *up, 1);
  114.             *upr = uid;
  115.         } else {
  116.             *up = AUTH_UID_NOBODY;
  117.         }
  118.     }
  119.     dprintf(D_UGID, "ruid(%s, %d) = %d\n",
  120.             inet_ntoa(mountp->client->clnt_addr), uid, *up);
  121.     return *up;
  122. }
  123.  
  124. /*
  125.  * Map a server gid to a client gid.
  126.  */
  127. gid_t rgid(gid, mountp, rqstp)
  128. gid_t        gid;
  129. nfs_mount    *mountp;
  130. struct svc_req    *rqstp;
  131. {
  132.     struct group    *gr;
  133.     ugid_map    *map;
  134.     gid_t        *gp, *gpr;
  135.     int        id;
  136.  
  137. #ifdef DOSHACKS
  138.     /* Reverse effects of all_squash for DOS clients */
  139.     if (mountp->o.all_squash && gid == mountp->o.nobody_gid)
  140.         return cred_gid;
  141. #endif
  142.     if (mountp->o.uidmap == identity)
  143.         return gid;
  144.  
  145.     map = getmap(mountp);
  146.     gp  = get_gid_mapent(&(map->gid), gid, 1);
  147.     if (*gp == AUTH_GID_NONE) {
  148.         if ((gr = getgrgid(gid)) != NULL &&
  149.             rlookup(gr->gr_name, &id, GROUP_GID, rqstp->rq_xprt) &&
  150.             (id != AUTH_GID_NONE)) {
  151.             *gp = id;
  152.             gpr = get_gid_mapent(&(map->rev_gid), *gp, 1);
  153.             *gpr = gid;
  154.         } else {
  155.             *gp = AUTH_GID_NOBODY;
  156.         }
  157.     }
  158.     dprintf(D_UGID, "rgid(%s, %d) = %d\n",
  159.             inet_ntoa(mountp->client->clnt_addr), gid, *gp);
  160.     return *gp;
  161. }
  162.  
  163. /*
  164.  * Map a client uid to a server uid
  165.  */
  166. uid_t luid(uid, mountp, rqstp)
  167. uid_t        uid;
  168. nfs_mount    *mountp;
  169. struct svc_req    *rqstp;
  170. {
  171.     struct passwd    *pw;
  172.     ugid_map    *map;
  173.     char        namebuf[MAXUGLEN];
  174.     uid_t        *up, *upr, retuid;
  175.     int        id;
  176.  
  177.     map = getmap(mountp);
  178.     if (mountp->o.uidmap == map_daemon) {
  179.         up = get_uid_mapent(&(map->rev_uid), uid, 1);
  180.         if (*up == AUTH_UID_NONE) {
  181.             id = uid;
  182.             if (rlookup(namebuf, &id, UID_NAME, rqstp->rq_xprt) &&
  183.                 ((pw = getpwnam(namebuf)) != NULL)) {
  184.                 *up = pw->pw_uid;
  185.                 upr = get_uid_mapent(&(map->uid), *up, 1);
  186.                 *upr = uid;
  187.             } else {
  188.                 *up = mountp->o.nobody_uid;
  189.             }
  190.         }
  191.         retuid = *up;
  192.     } else {
  193.         up = get_uid_mapent(&(map->rev_uid), uid, 0);
  194.         if (up != NULL && *up != AUTH_UID_NONE) {
  195.             if (*up == AUTH_UID_NOBODY)
  196.                 *up = mountp->o.nobody_uid;
  197.             retuid = *up;
  198.         } else {
  199.             retuid = uid;
  200.         }
  201.     }
  202.  
  203.     if ((retuid == 0 && mountp->o.root_squash) || mountp->o.all_squash)
  204.         retuid = mountp->o.nobody_uid;
  205.  
  206.     dprintf(D_UGID, "luid(%s, %d) = %d\n",
  207.             inet_ntoa(mountp->client->clnt_addr), uid, retuid);
  208.  
  209.     return retuid;
  210. }
  211.  
  212. /*
  213.  * Map a client gid to a server gid
  214.  */
  215. gid_t lgid(gid, mountp, rqstp)
  216. gid_t        gid;
  217. nfs_mount    *mountp;
  218. struct svc_req    *rqstp;
  219. {
  220.     struct group    *gr;
  221.     ugid_map    *map;
  222.     char        namebuf[MAXUGLEN]; 
  223.     gid_t        *gp, *gpr, retgid;
  224.     int        id;
  225.  
  226.     map = getmap(mountp);
  227.     if (mountp->o.uidmap == map_daemon) {
  228.         gp = get_gid_mapent(&(map->rev_gid), gid, 1);
  229.         if (*gp == AUTH_GID_NONE) {
  230.             id = gid;
  231.             if (rlookup(namebuf, &id, GID_GROUP, rqstp->rq_xprt) &&
  232.                 ((gr = getgrnam(namebuf)) != NULL)) {
  233.                 *gp = gr->gr_gid;
  234.                 gpr = get_gid_mapent(&(map->gid), *gp, 1);
  235.                 *gpr = gid;
  236.             } else {
  237.                 *gp = mountp->o.nobody_gid;
  238.             }
  239.         }
  240.         retgid = *gp;
  241.     } else {
  242.         gp = get_gid_mapent(&(map->rev_gid), gid, 0);
  243.         if (gp != NULL && *gp != AUTH_GID_NONE) {
  244.             if (*gp == AUTH_GID_NOBODY)
  245.                 *gp = mountp->o.nobody_gid;
  246.             retgid = *gp;
  247.         } else {
  248.             retgid = gid;
  249.         }
  250.     }
  251.  
  252.     if ((gid == 0 && mountp->o.root_squash) || mountp->o.all_squash)
  253.         retgid = mountp->o.nobody_gid;
  254.  
  255.     dprintf(D_UGID, "lgid(%s, %d) = %d\n",
  256.             inet_ntoa(mountp->client->clnt_addr), gid, retgid);
  257.  
  258.     return retgid;
  259. }
  260.  
  261. /*
  262.  * Define client to server mapping records for a given uid or gid.
  263.  */
  264. void ugid_map_uid(mountp, from, to)
  265. nfs_mount *mountp;
  266. uid_t    from;
  267. uid_t    to;
  268. {
  269.     ugid_map    *map;
  270.     uid_t        *up;
  271.  
  272.     dprintf(D_UGID, "%s:%s map uid rem %d -> loc %d\n",
  273.             mountp->client->clnt_name, mountp->path, from, to);
  274.     map = getmap(mountp);
  275.     up = get_uid_mapent(&(map->rev_uid), from, 1);
  276.     *up = to;
  277. }
  278.  
  279. void ugid_map_gid(mountp, from, to)
  280. nfs_mount *mountp;
  281. gid_t    from;
  282. gid_t    to;
  283. {
  284.     ugid_map    *map;
  285.     gid_t        *gp;
  286.  
  287.     dprintf(D_UGID, "%s:%s map gid rem %d -> loc %d\n",
  288.             mountp->client->clnt_name, mountp->path, from, to);
  289.     map = getmap(mountp);
  290.     gp = get_gid_mapent(&(map->rev_gid), from, 1);
  291.     *gp = to;
  292. }
  293.  
  294. /*
  295.  * Get the pointer to a uid map entry.
  296.  */
  297. static uid_t *get_uid_mapent(map, uid, create)
  298. struct uid_map    *map;
  299. uid_t         uid;
  300. int        create;
  301. {
  302.     uid_t    **tblp;
  303.     int    *limp, newlim, i;
  304.  
  305.     if (uid < UID_LIM1) {
  306.         tblp = &(map->bot);
  307.         limp = &(map->bot_lim);
  308.     } else {
  309.         uid = UID_MAX - uid;
  310.         tblp = &(map->top);
  311.         limp = &(map->top_lim);
  312.     }
  313.     if (uid >= *limp) {
  314.         if (!create) 
  315.             return NULL;
  316.  
  317.         newlim = uid + 1;
  318.         *tblp = (uid_t*) xrealloc(*tblp, newlim * sizeof(uid_t));
  319.         for (i = *limp; i < newlim; i++)
  320.             (*tblp)[i] = AUTH_UID_NONE;
  321.         *limp = newlim;
  322.     }
  323.     return (*tblp) + uid;
  324. }
  325.  
  326. /*
  327.  * Get the pointer to a gid map entry.
  328.  */
  329. static gid_t *get_gid_mapent(map, gid, create)
  330. struct gid_map    *map;
  331. gid_t        gid;
  332. int        create;
  333. {
  334.     gid_t    **tblp;
  335.     int    *limp, newlim, i;
  336.  
  337.     if (gid < GID_LIM1) {
  338.         tblp = &(map->bot);
  339.         limp = &(map->bot_lim);
  340.     } else {
  341.         gid = GID_MAX - gid;
  342.         tblp = &(map->top);
  343.         limp = &(map->top_lim);
  344.     }
  345.     if (gid >= *limp) {
  346.         if (!create)
  347.             return NULL;
  348.  
  349.         newlim = gid + 1;
  350.         *tblp = (gid_t*) xrealloc(*tblp, newlim * sizeof(gid_t));
  351.  
  352.         for (i = *limp; i < newlim; i++)
  353.             (*tblp)[i] = AUTH_GID_NONE;
  354.         *limp = newlim;
  355.     }
  356.     return (*tblp) + gid;
  357. }
  358.  
  359. /*
  360.  * Get the map for a given mount point. If it hasn't been initialized yet,
  361.  * create it.
  362.  */
  363. static ugid_map *getmap(mountp)
  364. nfs_mount *mountp;
  365. {
  366.     nfs_client    *clientp = mountp->client;
  367.     ugid_map    *map;
  368.  
  369.     if ((map = clientp->umap) != NULL) 
  370.         return map;
  371.  
  372.     map = (ugid_map *) xmalloc(sizeof(*map));
  373.  
  374.     map->uid.top         = map->uid.bot         = NULL;
  375.     map->uid.top_lim     = map->uid.bot_lim     = 0;
  376.     map->gid.top         = map->gid.bot         = NULL;
  377.     map->gid.top_lim     = map->gid.bot_lim     = 0;
  378.     map->rev_uid.top     = map->rev_uid.bot     = NULL;
  379.     map->rev_uid.top_lim = map->rev_uid.bot_lim = 0;
  380.     map->rev_gid.top     = map->rev_gid.bot     = NULL;
  381.     map->rev_gid.top_lim = map->rev_gid.bot_lim = 0;
  382.  
  383.     clientp->umap = map;
  384.     return map;
  385. }
  386.  
  387.  
  388. /*
  389.  * Deallocate a uid map.
  390.  */
  391. void
  392. ugid_free_map(map)
  393. ugid_map    *map;
  394. {
  395.     int    i;
  396.  
  397.     /* invalidate cache of client FH's */
  398.     if (initialized) {
  399.         for (i = 0; i < MAXCACHE; i++) {
  400.             if (cache[i].clnt != NULL)
  401.                 clnt_destroy(cache[i].clnt);
  402.             cache[i].addr.s_addr = INADDR_ANY;
  403.             cache[i].clnt = NULL;
  404.         }
  405.         initialized = 0;
  406.     }
  407.  
  408.     if (map->uid.top)    free (map->uid.top);
  409.     if (map->uid.bot)    free (map->uid.bot);
  410.     if (map->gid.top)    free (map->gid.top);
  411.     if (map->gid.bot)    free (map->gid.bot);
  412.     if (map->rev_uid.top)    free (map->rev_uid.top);
  413.     if (map->rev_uid.bot)    free (map->rev_uid.bot);
  414.     if (map->rev_gid.top)    free (map->rev_gid.top);
  415.     if (map->rev_gid.bot)    free (map->rev_gid.bot);
  416.  
  417.     free (map);
  418. }
  419.  
  420. /* 
  421.  * Obtain an RPC client handle for a given client host. We cache these
  422.  * handles on a limited scale.
  423.  */
  424. static CLIENT *getclnt(xprt)
  425. SVCXPRT        *xprt;
  426. {
  427.     struct sockaddr_in    addr;
  428.     struct timeval        wait;
  429.     CLIENT            *clnt;
  430.     time_t            now, age;
  431.     int            i, empty, oldest;
  432.     int            sock;
  433.  
  434.     if (!initialized) {
  435.         for (i = 0; i < MAXCACHE; i++) {
  436.             cache[i].addr.s_addr = INADDR_ANY;
  437.             cache[i].clnt = NULL;
  438.         }
  439.         initialized = 1;
  440.     }
  441.  
  442.     /* Get current time */
  443.     now = age = time(NULL);
  444.  
  445.     /* Check if the client is already cached */
  446.     addr = *svc_getcaller(xprt);
  447.     empty = oldest = -1;
  448.     for (i = 0; i < MAXCACHE; i++) {
  449.         if (cache[i].addr.s_addr == addr.sin_addr.s_addr)
  450.             break;
  451.         if (cache[i].clnt == NULL) {
  452.             empty = i;
  453.         } else if (cache[i].lru > age) {
  454.             age = cache[i].lru;
  455.             oldest = i;
  456.         }
  457.     }
  458.  
  459.     /* If the address was in the cache but the client was invalid,
  460.      * check if we should reattempt to obtain the handle
  461.      */
  462.     if (i < MAXCACHE) {
  463.         if ((clnt = cache[i].clnt) == NULL) {
  464.             if (now - cache[i].age <= EXPCACHE) {
  465.                 dprintf(D_UGID,
  466.                     "ugid: found invalid client %s\n",
  467.                     inet_ntoa(cache[i].addr));
  468.                 return NULL;
  469.             }
  470.             dprintf(D_UGID, "ugid: found expired client %s\n",
  471.                 inet_ntoa(cache[i].addr));
  472.             empty = i; i = MAXCACHE;    /* force lookup */
  473.         } else {
  474.             cache[i].lru = now;
  475.             return clnt;
  476.         }
  477.     }
  478.  
  479.     /* If not found and there's no empty slot, free the oldest */
  480.     if (i >= MAXCACHE && empty == -1) {
  481.         dprintf(D_UGID, "ugid: deleting oldest client %s slot %d\n",
  482.             inet_ntoa(cache[oldest].addr), oldest);
  483.         clnt_destroy(cache[oldest].clnt);
  484.         cache[oldest].clnt = NULL; 
  485.         empty = oldest;
  486.     }
  487.  
  488.     /* Client is not in cache. Create it. */
  489.     dprintf(D_UGID, "ugid: create client %s slot %d\n",
  490.         inet_ntoa(addr.sin_addr), empty);
  491.     cache[empty].clnt = NULL;
  492.     cache[empty].addr = addr.sin_addr;
  493.     cache[empty].age  = now;
  494.     cache[empty].lru  = now;
  495.  
  496.     addr.sin_port = 0;
  497.     wait.tv_sec   = 10;
  498.     wait.tv_usec  = 0;
  499.     sock = RPC_ANYSOCK;
  500.  
  501.     clnt = clntudp_create(&addr, UGIDPROG, UGIDVERS, wait, &sock);
  502.     if (clnt == NULL) {
  503.         dprintf(L_ERROR, "can't connect to ugidd on %s.\n", 
  504.                 inet_ntoa(addr.sin_addr));
  505.         cache[empty].clnt = NULL;
  506.         return NULL;
  507.     }
  508.  
  509.     /* I'm not sure if we can count on addr.sin_port to contain
  510.      * the server's port after clntudp_create, so we fetch it
  511.      * explicitly.
  512.      */
  513.     clnt_control(clnt, CLGET_SERVER_ADDR, &addr);
  514.     if (ntohs(addr.sin_port) >= IPPORT_RESERVED) {
  515.         dprintf(L_ERROR, "ugidd on %s runs on unprivileged port.\n",
  516.                 inet_ntoa(addr.sin_addr));
  517.         clnt_destroy(clnt);
  518.         cache[empty].clnt = NULL;
  519.         return NULL;
  520.     }
  521.  
  522.     cache[empty].clnt = clnt;
  523.     /* cache[empty].addr = addr.sin_addr; */
  524.  
  525.     return clnt;
  526. }
  527.  
  528. static void delete_clnt(clnt)
  529. CLIENT    *clnt;
  530. {
  531.     int        i;
  532.  
  533.     for (i = 0; i < MAXCACHE; i++) {
  534.         if (cache[i].clnt == clnt) 
  535.             break;
  536.     }
  537.     if (i < MAXCACHE) {
  538.         dprintf(L_ERROR,
  539.             "Call to ugidd on %s failed. Blocked for %d seconds.",
  540.             inet_ntoa(cache[i].addr), EXPCACHE);
  541.         clnt_destroy(clnt);
  542.         cache[i].clnt = NULL;
  543.         cache[i].age = cache[i].lru;
  544.     }
  545. }
  546.  
  547.  
  548. /*
  549.  * Lookup a given uid or gid by calling the client's ugidd.
  550.  *
  551.  * This incarnation of rlookup doesn't use the authenticate call
  552.  * anymore. This authentication required the ugidd server to open
  553.  * a priviled port and send an integer. This can be accomplished
  554.  * much more efficiently by requiring the server to run on a privileged
  555.  * port in the first place.
  556.  */
  557. static int rlookup(nam, id, map, xprt)
  558. char    *nam;
  559. int    *id;
  560. int    map;
  561. SVCXPRT    *xprt;
  562. {
  563.     CLIENT        *clnt;
  564.     int        *pi;
  565.     char        **sp;
  566.     int        ret = 0, retry = 0;
  567.  
  568.     if ((clnt = getclnt(xprt)) == NULL)
  569.         return 0;
  570.  
  571.     do {
  572.         switch (map) {
  573.         case NAME_UID:
  574.             pi = name_uid_1(&nam, clnt);
  575.             if ((ret = (pi != NULL)))
  576.                 *id = *pi;
  577.             dprintf(D_UGID, "rlookup(NAME_UID, %s) %s\n",
  578.                      nam, (pi != NULL)? "OK" : "FAIL");
  579.             break;
  580.         case GROUP_GID:
  581.             pi = group_gid_1(&nam, clnt);
  582.             if ((ret = (pi != NULL)))
  583.                 *id = *pi;
  584.             dprintf(D_UGID, "rlookup(GROUP_GID, %s) %s\n",
  585.                      nam, (pi != NULL)? "OK" : "FAIL");
  586.             break;
  587.         case UID_NAME:
  588.             sp = uid_name_1(id, clnt);
  589.             if ((ret = (sp != NULL)))
  590.                 strcpy(nam, *sp);
  591.             dprintf(D_UGID, "rlookup(UID_NAME, %d) %s\n",
  592.                     *id, (sp != NULL)? "OK" : "FAIL");
  593.             break;
  594.         case GID_GROUP:
  595.             sp = gid_group_1(id, clnt);
  596.             if ((ret = (sp != NULL)))
  597.                 strcpy(nam, *sp);
  598.             dprintf(D_UGID, "rlookup(GID_GROUP, %d) %s\n",
  599.                     *id, (sp != NULL)? "OK" : "FAIL");
  600.             break;
  601.         default:
  602.             return 0;
  603.         }
  604.  
  605.         /* RPC error - check the status. When encountering errors that
  606.          * are likely to persist, we clear the client to make sure 
  607.          * no more lookups are attempted within the next EXPCACHE 
  608.          * seconds.
  609.          */
  610.         if (!ret) {
  611.             struct rpc_err    err;
  612.  
  613.             dprintf(D_UGID, "ugidd call error: %s\n",
  614.                 clnt_sperror(clnt, ""));
  615.  
  616.             clnt_geterr(clnt, &err);
  617.             switch (err.re_status) {
  618.             case RPC_CANTSEND:    /* Maybe network failures */
  619.             case RPC_CANTRECV:    /* should be transient */
  620.             case RPC_TIMEDOUT:    /* This is the worst one */
  621.             case RPC_VERSMISMATCH:    /* Cases of general bogosity */
  622.             case RPC_AUTHERROR:
  623.             case RPC_PROGVERSMISMATCH:
  624.             case RPC_PROCUNAVAIL:
  625.                 dprintf(D_UGID, "deleting client %lx\n", clnt);
  626.                 delete_clnt(clnt);
  627.                 break;
  628.             case RPC_CANTDECODEARGS:/* retry operation */
  629.             case RPC_CANTDECODERES:
  630.                 dprintf(D_UGID, "retrying operation (%d)\n",
  631.                             retry);
  632.                 retry++;
  633.                 break;
  634.             default:
  635.                 break;
  636.             }
  637.         }
  638.     } while (retry && retry < 3);
  639.  
  640.     return ret;
  641. }
  642.